/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.designtime.generator.entitydataconvertor.method;

import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bff.designtime.generator.common.GenUtils;
import com.inspur.edp.bff.designtime.generator.entitydataconvertor.VMObjectDataConvertorGenContext;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.generator.DotNetToJavaStringHelper;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import lombok.var;
import org.eclipse.jdt.core.dom.*;

import java.util.ArrayList;
import java.util.Arrays;

public class MappingDataFormVoGenerator extends ClassMethodGenerator {

    private static final String ParamTransBeData = "_beData";
    private static final String ParamBeData = "beData";
    private static final String ParamTransVoData = "_vmData";
    private static final String ParamVoData = "vmData";
    private VMObjectDataConvertorGenContext parentGenContext;

    public MappingDataFormVoGenerator(VMObjectDataConvertorGenContext parentGenContext) {
        this.parentGenContext = parentGenContext;
    }

    @Override
    protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
        return new ArrayList(Arrays.asList(
            new Modifier.ModifierKeyword[]{Modifier.ModifierKeyword.PROTECTED_KEYWORD}));
    }

    @Override
    protected boolean getIsOverride() {
        return true;
    }

    @Override
    protected String getMethodName() {
        return "mappingEntityDataPropertiesFromVo";
    }

    @Override
    protected ArrayList<ParameterInfo> getParameterCollection() {
        ArrayList<ParameterInfo> list = new ArrayList();
        TypeInfo typeInfo = new TypeInfo("IEntityData");
        typeInfo.setTypePackageName("com.inspur.edp.cef.entity.entity");


        ParameterInfo paramBeData = new ParameterInfo();
        paramBeData.setParamType(typeInfo);
        paramBeData.setParamName(ParamBeData);

        list.add(paramBeData);
        ParameterInfo paramVoData = new ParameterInfo();
        paramVoData.setParamType(typeInfo);
        paramVoData.setParamName(ParamVoData);
        list.add(paramVoData);
        return list;
    }

    @Override
    protected TypeInfo getReturnType() {
        return new TypeInfo("void");
    }


    @Override
    protected Block buildMethodBody() {
        Block blockSyntax = getTypeAsBlock();

        for (IGspCommonField gspCommonField : parentGenContext.getVmObject().getContainElements()) {

            GspViewModelElement item = (GspViewModelElement) gspCommonField;
            if (item.getIsVirtual()) {
                continue;
            }
            if (DotNetToJavaStringHelper.isNullOrEmpty(item.getMapping().getTargetObjId()))
            //TODO 异常封装
            {
                throw new RuntimeException(
                    "#GspBefError# 字段" + item.getCode() + " 的Mapping字段ID不允许为空#GspBefError#");
            }

            if (item.getMapping().getTargetMetadataId() .equals(parentGenContext.getVmGeneratorContext()
                .getVm().getMapping().getTargetMetadataId()) == false) {
                continue;
            }

            Object tempVar = parentGenContext.getTargetObject().findElement(item.getMapping().getTargetObjId());
            GspBizEntityElement entityElement = (GspBizEntityElement) (
                (tempVar instanceof GspBizEntityElement) ? tempVar : null);
            if (entityElement == null) {
                continue;
            }

            addElementMapBlock(item, entityElement, blockSyntax);
        }
        return blockSyntax;
    }

    private Block getTypeAsBlock() {
        Block block = ast.newBlock();
        String beTypeName = null;
        TypeInfo beType = parentGenContext.getBEDataType(beTypeName);
        addImport(beType.getTypeFullName());
        String voType = parentGenContext.getVmObject().getGeneratedEntityClassInfo().getClassName();

        Statement beDataStatement = getTransDataStatement(beType.getTypeName(), ParamTransBeData, ParamBeData);
        Statement voDataStatement = getTransDataStatement(voType, ParamTransVoData, ParamVoData);

        block.statements().add(beDataStatement);
        block.statements().add(voDataStatement);

        return block;
    }

    private Statement getTransDataStatement(String typeName, String varName, String paramName) {

        VariableDeclarationFragment frag = ast.newVariableDeclarationFragment();
        frag.setName(ast.newSimpleName(varName));
        CastExpression castStatement = ast.newCastExpression();
        castStatement.setType(ast.newSimpleType(ast.newSimpleName(typeName)));
        castStatement.setExpression(ast.newSimpleName(paramName));
        frag.setInitializer(castStatement);

        VariableDeclarationStatement statement = ast.newVariableDeclarationStatement(frag);
        statement.setType(ast.newSimpleType(ast.newSimpleName(typeName)));

        return statement;
    }

    private void addElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement, Block block) {
        if (vmElement.getIsUdt()) {
            addUdtElementMapBlock(vmElement, beElement,block);
            return;
        }

        switch (vmElement.getObjectType()) {
            case None:
            case DynamicProp:
                addBasicElementMapBlock(vmElement, beElement, block);
                break;
            case Association:
                addAssElementMapBlock(vmElement, beElement, block);
                break;
            case Enum:
                addEnumElementMapBlock(vmElement, beElement, block);
                break;
            default:
                throw new RuntimeException(
                    "#GspBefError# 错误的类型" + vmElement.getObjectType().toString()
                        + " #GspBefError# ");
        }
    }


    ///#region Udt

    private void addUdtElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement,Block block) {
        if (vmElement.getObjectType() == GspElementObjectType.Association) {
            if(vmElement.containRefElementNotFromAssoUdt()){
                addAssElementMapBlock(vmElement, beElement, block);
            }else if(beElement.containRefElementNotFromAssoUdt()){

            }else{
                addBasicElementMapBlock(vmElement, beElement, block);
            }
        } else {
            addBasicElementMapBlock(vmElement, beElement, block);
        }
    }

    // endregion

    // region Normal
    private void addBasicElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement, Block blockSyntax) {

        MethodInvocation invocation = ast.newMethodInvocation();
        invocation.setExpression(ast.newSimpleName(ParamTransBeData));
        invocation.setName(ast.newSimpleName("set" + beElement.getLabelID()));
        invocation.arguments().add(ExpressionUtils.getMethodInvocation(ast, ParamTransVoData, "get" + vmElement.getLabelID()));

        blockSyntax.statements().add(ast.newExpressionStatement(invocation));
    }
    // endregion

    // region Association

    private void addAssElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement, Block block) {
        ifNullBlock(vmElement, beElement, block);
    }

    private void ifNullBlock(GspViewModelElement vmElement, GspBizEntityElement beElement, Block block) {

        IfStatement ifStatement = ast.newIfStatement();
        InfixExpression condition = ast.newInfixExpression();
        MethodInvocation leftStatement = ast.newMethodInvocation();
        leftStatement.setExpression(ast.newSimpleName(ParamTransVoData));
        leftStatement.setName(ast.newSimpleName("get" + vmElement.getLabelID()));
        condition.setLeftOperand(leftStatement);
        condition.setRightOperand(ast.newNullLiteral());
        condition.setOperator(InfixExpression.Operator.EQUALS);
        ifStatement.setExpression(condition);

        Block thenBlock = ast.newBlock();
        MethodInvocation thenInvocation = ast.newMethodInvocation();
        thenInvocation.setExpression(ast.newSimpleName(ParamTransBeData));
        thenInvocation.setName(ast.newSimpleName("set" + beElement.getLabelID()));
        thenInvocation.arguments().add(ast.newNullLiteral());
        thenBlock.statements().add(ast.newExpressionStatement(thenInvocation));

        ifStatement.setThenStatement(thenBlock);


        Block elseblock = buildAssElseStatement(vmElement, beElement);
        ifStatement.setElseStatement(elseblock);
        block.statements().add(ifStatement);
    }

    private Block buildAssElseStatement(GspViewModelElement vmElement, GspBizEntityElement beElement) {

        Block block = ast.newBlock();
        if (vmElement.getIsUdt() && beElement.containRefElementNotFromAssoUdt()) {
            String valName = "voAssoInfo";
            Statement getAssoInfoStatement = getAssoInfoStatement(vmElement,
                beElement, valName);
            ExpressionStatement udtStatement = getUdtStatement(beElement, valName);

            block.statements().add(getAssoInfoStatement);
            block.statements().add(udtStatement);
        } else {
            ExpressionStatement initStatement = addNewBlock(beElement);
            ExpressionStatement keyStatement = getAssKeyStatement(vmElement, beElement);
            block.statements().add(initStatement);
            block.statements().add(keyStatement);
        }
        assPropMapBlock(vmElement, beElement, block);
        return block;
    }

    private ExpressionStatement addNewBlock(GspBizEntityElement beElement) {

        String assoTypeName = null;
        TypeInfo assType = GenUtils
            .GetBEAssoType(parentGenContext.getVmGeneratorContext(), beElement, assoTypeName);

        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setExpression(ast.newSimpleName(ParamTransBeData));
        methodInvocation.setName(ast.newSimpleName("set" + beElement.getLabelID()));
        ClassInstanceCreation creation = ast.newClassInstanceCreation();
        creation.setType(ast.newSimpleType(ast.newQualifiedName(
            ast.newName(assType.getTypePackageName()),
            ast.newSimpleName(assType.getTypeName()))));
        methodInvocation.arguments().add(creation);

        return ast.newExpressionStatement(methodInvocation);
    }

    private ExpressionStatement getAssKeyStatement(GspViewModelElement vmElement, GspBizEntityElement beElement) {

        MethodInvocation invocation = ast.newMethodInvocation();

        MethodInvocation getterInvocation = ast.newMethodInvocation();
        getterInvocation.setExpression(ast.newSimpleName(ParamTransBeData));
        getterInvocation.setName(ast.newSimpleName("get" + beElement.getLabelID()));
        invocation.setExpression(getterInvocation);
        invocation.setName(ast.newSimpleName("set" + beElement.getLabelID()));

        MethodInvocation arg = ast.newMethodInvocation();
        MethodInvocation argGetInvocation = ast.newMethodInvocation();
        argGetInvocation.setExpression(ast.newSimpleName(ParamTransVoData));
        argGetInvocation.setName(ast.newSimpleName("get" + vmElement.getLabelID()));
        arg.setExpression(argGetInvocation);
        arg.setName(ast.newSimpleName("get" + vmElement.getLabelID()));

        invocation.arguments().add(arg);

        return ast.newExpressionStatement(invocation);
    }

    private void assPropMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement,
        Block blockSyntax) {

        for (IGspCommonField refElement : vmElement.getChildAssociations().get(0).getRefElementCollection()) {
            GspViewModelElement item = (GspViewModelElement) refElement;

            if (item.getIsFromAssoUdt()) {
                continue;
            }

            GspBizEntityElement beAssElement = (GspBizEntityElement) (
                (beElement.getChildAssociations().get(0).getRefElementCollection()
                    .getItem(item.getMapping().getTargetObjId()) instanceof GspBizEntityElement)
                    ? beElement.getChildAssociations().get(0).getRefElementCollection()
                    .getItem(item.getMapping().getTargetObjId())
                    : null);


            MethodInvocation voData = ExpressionUtils.getMethodInvocation(ast, "_vmData", "get" + vmElement.getLabelID());
            MethodInvocation beData = ExpressionUtils.getMethodInvocation(ast, "_beData", "get" + beElement.getLabelID());

            if (vmElement.getIsUdt()) {
                beData = ExpressionUtils.getMethodInvocation(ast,beData, "get" + beElement.getLabelID());
            }

            voData = ExpressionUtils.getMethodInvocation(ast,voData, "get" + item.getLabelID());
            beData = ExpressionUtils.getMethodInvocation(ast,beData, "set" + beAssElement.getLabelID());

            beData.arguments().add(voData);
            blockSyntax.statements().add(ast.newExpressionStatement(beData));
        }
    }

    private Statement getAssoInfoStatement(GspViewModelElement voElement, GspBizEntityElement beElement, String valName) {

        String assoTypeName = voElement.getAssociationTypeName();
        VariableDeclarationFragment frag = ast.newVariableDeclarationFragment();
        frag.setName(ast.newSimpleName(valName));
        MethodInvocation invocation = ExpressionUtils.getMethodInvocation(ast,ParamTransVoData, "get" + voElement.getLabelID());
        invocation = ExpressionUtils.getMethodInvocation(ast,invocation, "get" + beElement.getLabelID());

        frag.setInitializer(invocation);

        VariableDeclarationStatement statement = ast.newVariableDeclarationStatement(frag);
        statement.setType(ast.newSimpleType(ast.newSimpleName(assoTypeName)));
        return statement;
    }

    private ExpressionStatement getUdtStatement(GspBizEntityElement beElement, String valName) {
        String associatTypeName = null;
        TypeInfo assType = GenUtils
            .GetBEAssoType(parentGenContext.getVmGeneratorContext(), beElement, associatTypeName);


        MethodInvocation incocation = ExpressionUtils.getMethodInvocation(ast,ParamTransBeData, "get" + beElement.getLabelID());
        incocation = ExpressionUtils.getMethodInvocation(ast,incocation, "set"+beElement.getLabelID());

        ClassInstanceCreation arg = ast.newClassInstanceCreation();
        arg.setType(ast.newSimpleType(ast.newQualifiedName(ast.newName(assType.getTypePackageName()),ast.newSimpleName(assType.getTypeName()))));
        arg.arguments().add(ast.newSimpleName(valName));

        incocation.arguments().add(arg);

        return ast.newExpressionStatement(incocation);
    }

    //endregion

    //region Enum
    private void addEnumElementMapBlock(GspViewModelElement vmElement, GspBizEntityElement beElement, Block blockSyntax) {
        IfStatement ifStatement = ast.newIfStatement();
        MethodInvocation leftOperan = ast.newMethodInvocation();
        leftOperan.setExpression(ast.newSimpleName(ParamTransBeData));
        leftOperan.setName(ast.newSimpleName("get" + vmElement.getLabelID()));
        InfixExpression infixExpression =  ast.newInfixExpression();
        infixExpression.setLeftOperand(leftOperan);
        infixExpression.setOperator(InfixExpression.Operator.EQUALS);
        infixExpression.setRightOperand(ast.newNullLiteral());
        ifStatement.setExpression(infixExpression);


        Block thenBlock = ast.newBlock();
        MethodInvocation thenMethod = ast.newMethodInvocation();
        thenMethod.setExpression(ast.newSimpleName(ParamTransVoData));
        thenMethod.setName(ast.newSimpleName("set" + vmElement.getLabelID()));
        thenMethod.arguments().add(ast.newNullLiteral());
        ExpressionStatement expressionStatement = ast.newExpressionStatement(thenMethod);
        thenBlock.statements().add(expressionStatement);
        ifStatement.setThenStatement(thenBlock);

        String enumClassName = null;
        TypeInfo enumType = GenUtils
            .getBeEnumType(parentGenContext.getVmGeneratorContext(), beElement, enumClassName);

        MethodInvocation invocation = ast.newMethodInvocation();
        invocation.setExpression(ast.newSimpleName(ParamTransBeData));
        invocation.setName(ast.newSimpleName("set" + beElement.getLabelID()));

        ArrayAccess arg = ast.newArrayAccess();

        MethodInvocation array = ast.newMethodInvocation();
        array.setExpression(ast.newQualifiedName(
            ast.newName(enumType.getTypePackageName()), ast.newSimpleName(enumType.getTypeName())));
        array.setName(ast.newSimpleName("values"));
        arg.setArray(array);

        MethodInvocation index = ast.newMethodInvocation();
        MethodInvocation expression = ast.newMethodInvocation();
        expression.setExpression(ast.newSimpleName(ParamTransVoData));
        expression.setName(ast.newSimpleName("get" + vmElement.getLabelID()));
        index.setExpression(expression);
        index.setName(ast.newSimpleName("ordinal"));
        arg.setIndex(index);

        invocation.arguments().add(arg);
        Block elseBlock = ast.newBlock();
        elseBlock.statements().add(ast.newExpressionStatement(invocation));
        ifStatement.setElseStatement(elseBlock);

        blockSyntax.statements().add(ifStatement);
    }

    // endregion
}
